#include <mutex>

#include <QUndoStack>
#include <QJsonObject>
#include <QJsonArray>
#include <QTabWidget>

#include <osg/CullFace>
#include <osg/Point>
#include <osg/ComputeBoundsVisitor>
#include <osg/Group>
#include <osg/FrontFace>

#include <Geom_Curve.hxx>
#include <GeomAPI_ExtremaCurveCurve.hxx>
#include <Geom_Curve.hxx>
#include <BRepTools.hxx>
#include <TopoDS_Face.hxx>

#include "Log.h"
#include "Qt2DEnt.h"
#include "Qt2DWidget.h"
#include "QtOSGWidget.h"
#include "Qt3DPicker.h"

#include "Scene.h"
#include "SelectHandlerBase.h"
#include "Registry.h"
#include "MetaObjectManager.h"
#include "GLSLPrograms.h"
#include "OsgUtil.h"
#include "ActorTable.h"
#include "JsonReader.h"
#include "Ucs.h"
#include "Ent.h"
#include "MyPoint.h"
#include "Polygon.h"
#include "Surface.h"
#include "HandlesGeometry.h"
#include "QtOSGWidget.h"
#include "ViewerExt.h"
#include "CmdHandler3.h"

class Root3DCallback : public osg::NodeCallback
{
	Scene* m_scene;
public:
	Root3DCallback(Scene* scene)
		: m_scene(scene)
	{
		assert(scene);
	}
	void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		if (nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
		{
			m_scene->update3D();
		}
		// must call any nested node callbacks and continue subgraph traversal.
		NodeCallback::traverse(node, nv);
	}
};

// gridLen = 0.25 * 2 ^ n
int getGridLenPower(double maxSize/*>0*/)
{
	double gridLen = 0.25;
	int n = 1;
	maxSize /= 20.0;
	while (gridLen < maxSize)
	{
		gridLen *= 2;
		++n;
	}
	return n;
}

enum AxisName { AX, AY, AZ, AN = -1 };
AxisName getAxisName(osg::Vec3d& dir)
{
	dir.x() = std::abs(dir.x());
	dir.y() = std::abs(dir.y());
	dir.z() = std::abs(dir.z());
	AxisName  r = AN;
	if ((dir - osg::Vec3d(1, 0, 0)).length2() < 1E-14)
		r = AX;
	else if ((dir - osg::Vec3d(0, 1, 0)).length2() < 1E-14)
		r = AY;
	else if ((dir - osg::Vec3d(0, 0, 1)).length2() < 1E-14)
		r = AZ;
	return r;
}

class UCSMeshCallback : public osg::NodeCallback
{
	Scene* m_scene;
public:
	UCSMeshCallback(Scene* scene)
		: m_scene(scene)
	{
		assert(scene);
	}
	void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		if (nv->getVisitorType() == osg::NodeVisitor::EVENT_VISITOR)
		{
			auto ev = nv->asEventVisitor();
			const auto& events = ev->getEvents();
			for (const auto& event : events)
			{
				const auto ea = event->asGUIEventAdapter();
				if (ea && ea->getEventType() == osgGA::GUIEventAdapter::FRAME)
				{
					bool calcMesh = true;
					auto ucses = m_scene->getUCS();
					for (const auto& ucs : ucses)
					{
						if (!ucs)
							calcMesh = false;
					}
					if (calcMesh)
					{
						Registry &reg = Registry::instance();
						auto widget3D = reg.get<QtOSGWidget>(widget3DID);
						auto camera = widget3D->getCamera();

						// 缺省绘制静态网格
						int maxX, maxY, maxZ;
						m_scene->getRange(maxX, maxY, maxZ);
						int nx = getGridLenPower(maxX);
						int ny = getGridLenPower(maxY);
						int nz = getGridLenPower(maxZ);
						int n = (nx + ny + nz) / 3;
						double gridLen = 0.25 * std::pow(2, n);
						int minXI = 0, maxXI = std::max(int(maxX / gridLen), 1);
						int minYI = 0, maxYI = std::max(int(maxY / gridLen), 1);
						int minZI = 0, maxZI = std::max(int(maxZ / gridLen), 1);
						

						double left, right, bottom, top, zNear, zFar;
						bool success = camera->getProjectionMatrixAsOrtho(left, right, bottom, top, zNear, zFar);
						if (success)
						{
							if (isnan<double>(zFar))
							{
							}
							else
							{
								const osg::Matrixd& viewMatrix = camera->getViewMatrix();
								// 方向变换，不是点变换，所以去掉平移分量。注意transform3x3参数的顺序
								auto dir1 = osg::Matrixd::transform3x3(osg::Vec3d(1, 0, 0), viewMatrix);
								auto dir2 = osg::Matrixd::transform3x3(osg::Vec3d(0, 1, 0), viewMatrix);
								AxisName dir1AxisName = getAxisName(dir1);
								AxisName dir2AxisName = getAxisName(dir2);
								if (dir1AxisName != AN && dir2AxisName != AN)
								{
									osg::Matrixd VPW = camera->getViewport()->computeWindowMatrix();
									VPW.preMult(camera->getProjectionMatrix());
									VPW.preMult(camera->getViewMatrix());
									auto VPW_1 = osg::Matrixd::inverse(VPW);
									auto vp = camera->getViewport();
									auto width = vp->width();
									auto height = vp->height();
									auto pt1 = osg::Vec3d(0, 0, 0) * VPW_1;
									auto pt2 = osg::Vec3d(width, height, 0) * VPW_1;
									auto v = pt2 - pt1;
									if (std::abs(v.x()) < 1E-12)
									{
										maxXI = 0;
										int ny = getGridLenPower(v.y());
										int nz = getGridLenPower(v.z());
										int n = (ny + nz) / 2;
										gridLen = 0.25 * std::pow(2, n);
										maxYI = ceil(std::max(pt1.y(), pt2.y()) / gridLen);
										maxZI = ceil(std::max(pt1.z(), pt2.z()) / gridLen);
										minYI = floor(std::min(pt1.y(), pt2.y()) / gridLen);
										minZI = floor(std::min(pt1.z(), pt2.z()) / gridLen);
									}
									else if (std::abs(v.y()) < 1E-12)
									{
										maxYI = 0;
										int nx = getGridLenPower(v.x());
										int nz = getGridLenPower(v.z());
										int n = (nx + nz) / 2;
										gridLen = 0.25 * std::pow(2, n);
										maxXI = ceil(std::max(pt1.x(), pt2.x()) / gridLen);
										maxZI = ceil(std::max(pt1.z(), pt2.z()) / gridLen);
										minXI = floor(std::min(pt1.x(), pt2.x()) / gridLen);
										minZI = floor(std::min(pt1.z(), pt2.z()) / gridLen);
									}
									else
									{
										maxZI = 0;
										int nx = getGridLenPower(v.x());
										int ny = getGridLenPower(v.y());
										int n = (nx + ny) / 2;
										gridLen = 0.25 * std::pow(2, n);
										maxXI = ceil(std::max(pt1.x(), pt2.x()) / gridLen);
										maxYI = ceil(std::max(pt1.y(), pt2.y()) / gridLen);
										minXI = floor(std::min(pt1.x(), pt2.x()) / gridLen);
										minYI = floor(std::min(pt1.y(), pt2.y()) / gridLen);
									}
								}
							}
						}

						for (int i = 0; i < ucses.size(); ++i)
						{
							UCSKind kind = m_scene->getCurrentUCSIndex();
							osg::Vec4 color;
							if (kind == ucses[i]->getKind())
								color = { 0.6, 0.2, 0.2, 1 };
							else
								color = { 0.4, 0.4, 0.4, 1 };
							int xFrom, xTo, yFrom, yTo;
							switch (ucses[i]->getKind())
							{
							case UCSKind::XOY:
								xFrom = minXI;
								xTo = maxXI;
								yFrom = minYI;
								yTo = maxYI;
								break;
							case UCSKind::YOZ:
								xFrom = minYI;
								xTo = maxYI;
								yFrom = minZI;
								yTo = maxZI;
								break;
							case UCSKind::ZOX:
								xFrom = minXI;
								xTo = maxXI;
								yFrom = minZI;
								yTo = maxZI;
								break;
							}
							ucses[i]->updateMesh(color, gridLen, xFrom, xTo, yFrom, yTo);
						}
						break;
					}
				}
			}
		}
		// must call any nested node callbacks and continue subgraph traversal.
		NodeCallback::traverse(node, nv);
	}
};

struct ScenePrivate
{
	ScenePrivate()
		: m_needSort(false)
		, m_undoStack(nullptr)
		, m_picker(nullptr)
	{}
	~ScenePrivate()
	{
		if (m_picker)
			m_picker->disconnectUserFeedback(m_userFeedbackConn);
	}
	std::set<std::shared_ptr<INode>> m_children;
	mutable std::vector<int> m_sortedIdTable; // ID从小到大排序

	mutable osg::ref_ptr<osg::Group> m_rep3D;
	mutable osg::ref_ptr<osg::Group> m_root3D;
	mutable osg::ref_ptr<osg::Geometry> m_snapPoint;
	osg::Vec3d m_snapPointPos;
	bool m_showSnapPoint = false;
	bool m_needSort;
	QUndoStack* m_undoStack;
	QPointer<Qt3DPicker> m_picker;
	std::mutex m_mutex;
	std::shared_ptr<UCS> m_ucses[3];
	int m_currentUCSIndex = 0;
	int m_maxX = 10;
	int m_maxY = 10;
	int m_maxZ = 10;

	osg::ref_ptr<HandlesGeometry> m_osgHandles;
	int m_userFeedbackConn = 0;
	mutable std::vector<QMetaObject::Connection> m_connections;
	bool m_allowBuild3DXPoints = true;
	bool m_needBuild3DXPoints = false;
	mutable std::vector<osg::Vec3d> m_3DXpoints;
	bool m_ccw = true;
};

Scene::Scene(QObject* parent, QUndoStack* undoStack)
	: IRoot()
	, d_ptr(new ScenePrivate)
{
	d_ptr->m_undoStack = undoStack;
	// 我们是单文档，Scene对象代表了一个文档，新建文档要清空操作历史
	if (undoStack)
	{
		assert(undoStack->count() == 0);
	}
	setParent(parent);
	getRoot3D();
}

Scene::~Scene()
{
	// MainWindow::setScene会调用removePostDraw(snapPoint)
	removeChildren();
	if (d_ptr)
		delete d_ptr;
}

void Scene::addChild(const std::shared_ptr<INode>& pChild)
{
	addChildImpl(pChild, d_ptr->m_children, d_ptr->m_sortedIdTable);
}

void Scene::removeChild(const std::shared_ptr<INode>& pChild)
{
	removeChildImpl(pChild, d_ptr->m_children, d_ptr->m_sortedIdTable);
}

void Scene::removeChildren()
{
	removeChildrenImpl(d_ptr->m_children, d_ptr->m_sortedIdTable);
}

const std::set<std::shared_ptr<INode>>& Scene::getChildren() const
{
	return d_ptr->m_children;
}

void Scene::updateColor()
{
	const auto& children = getChildren();
	for (const auto& child : children)
	{
		if (child->isLeaf())
		{
			auto ent = std::dynamic_pointer_cast<Ent>(child);
			ent->updateColor();
		}
		else
		{
			// face都在Scene下面，不应该在UCS里面
		}
	}
}

int Scene::modelRow2ConstructID(int row) const
{
	return d_ptr->m_sortedIdTable[row];
}

int Scene::constructID2ModelRow(int id) const
{
	return getModelRow(d_ptr->m_sortedIdTable, id);
}

void Scene::visitChildren(INodeCallback lcb, bool preorder) const
{
	visitChildrenImpl(lcb, d_ptr->m_children, preorder);
}

void Scene::showSnapPoint(const osg::Vec3d & pos)
{
	Registry &reg = Registry::instance();
	auto widget3D = reg.get<QtOSGWidget>(widget3DID);
	auto viewer = dynamic_cast<ViewerExt *>(widget3D->getViewer());
	if (!viewer)
		return;
	auto cam = viewer->getCamera();
	if (!cam)
		return;
	osg::Matrix VPW = cam->getViewport()->computeWindowMatrix();
	VPW.preMult(cam->getProjectionMatrix());
	VPW.preMult(cam->getViewMatrix());
	d_ptr->m_snapPointPos = pos * VPW;
	// 修复Release下大尺寸构件，例如异形承台，捕捉点不显示的错误
	d_ptr->m_snapPointPos.z() = 0;
	d_ptr->m_showSnapPoint = true;
}

void updateSnapPoint(const osg::ref_ptr<osg::Geometry>& m_snapPoint, const osg::Vec3d& m_snapPointPos, bool show)
{
	m_snapPoint->setNodeMask(show ? NoPickMask : 0);
	auto pts = static_cast<osg::Vec3Array*>(m_snapPoint->getVertexArray());
	if (pts->at(0) != m_snapPointPos)
	{
		pts->at(0) = m_snapPointPos;
		pts->dirty();
		m_snapPoint->dirtyGLObjects();
	}
}

void Scene::hideSnapPoint()
{
	d_ptr->m_showSnapPoint = false;
}

osg::ref_ptr<osg::Geometry> Scene::getSnapPoint() const
{
	return d_ptr->m_snapPoint;
}

std::vector<gp_Pnt> intersect3D(Handle(Geom_Curve) curve1, Handle(Geom_Curve) curve2)
{
	std::vector<gp_Pnt> result;
	GeomAPI_ExtremaCurveCurve ecc;
	ecc.Init(curve1, curve2);
	for (int i = 1; i <= ecc.NbExtrema(); ++i)
	{
		gp_Pnt p1, p2;
		ecc.Points(i,p1,p2);
		p1.SetCoord((p1.X() + p2.X()) / 2.0, (p1.Y() + p2.Y()) / 2.0, (p1.Z() + p2.Z()) / 2.0);
		result.push_back(p1);
	}
	return result;
}

void Scene::build3DXPoints()
{
	std::vector<Handle(Geom_Curve)> curves;
	const auto& children = getChildren();
	for (const auto& child : children)
	{
		auto curve = std::dynamic_pointer_cast<Ent3DCurve>(child);
		if (curve)
		{
			auto tmp = curve->getCurve3d();
			curves.insert(curves.end(), tmp.begin(), tmp.end());
		}
	}
	std::vector<osg::Vec3d> result;
	int curveCount = curves.size();
	for (int i = 0; i < curveCount - 1; ++i)
	{
		for (int j = i + 1; j < curveCount; ++j)
		{
			std::vector<gp_Pnt> points = intersect3D(curves[i], curves[j]);
			for (const auto& pt : points)
			{
				result.push_back(osg::Vec3d(pt.X(), pt.Y(),pt.Z()));
			}
		}
	}
	d_ptr->m_3DXpoints.swap(result);
}

std::vector<osg::Vec3d> Scene::get3DXPoints()
{
	return d_ptr->m_3DXpoints;
}

void Scene::pauseBuild3DXPoints()
{
	d_ptr->m_allowBuild3DXPoints = false;
}

void Scene::restoreBuild3DXPoints()
{
	d_ptr->m_allowBuild3DXPoints = true;
	if (d_ptr->m_needBuild3DXPoints)
		addFlags(UPDATE_CHILDREN); //Scene::update3D > build3DXPoints will be called
}

void Scene::update3D()
{
	//TopoSort();
	if (flags() & UPDATE_CHILDREN)
	{
		update3DChildrenImpl(d_ptr->m_children);
		if (d_ptr->m_allowBuild3DXPoints)
		{
			build3DXPoints();
		}
		d_ptr->m_needBuild3DXPoints = !d_ptr->m_allowBuild3DXPoints;
	}
	//update3DChildrenImpl(d_ptr->m_children);
	removeFlags(0xFFFFFFFF);
	updateSnapPoint(d_ptr->m_snapPoint, d_ptr->m_snapPointPos, d_ptr->m_showSnapPoint);
}

osg::Node * Scene::getRep3D() const
{
	getRoot3D();
	return d_ptr->m_rep3D.get();
}

osg::Node * Scene::getRoot3D() const
{
	if (!d_ptr->m_rep3D)
	{
		d_ptr->m_rep3D = new osg::Group;
		// call update3D on each frame.
		d_ptr->m_rep3D->setUpdateCallback(new Root3DCallback(const_cast<Scene*>(this)));
		// call UCS::updateMesh on each frame.
		d_ptr->m_rep3D->setEventCallback(new UCSMeshCallback(const_cast<Scene*>(this)));

		// 捕捉点OSG对象
		osg::ref_ptr<osg::Geometry> snapPoint = new osg::Geometry();
		d_ptr->m_snapPoint = snapPoint;
		//初始不可见
		snapPoint->setNodeMask(0);
		auto pts = new osg::Vec3Array;
		pts->push_back(osg::Vec3d());
		snapPoint->setVertexArray(pts);
		auto colors = new osg::Vec4Array(osg::Array::BIND_OVERALL);
		colors->push_back(osg::Vec4(1.f, 0.f, 0.f, 1.f));
		snapPoint->setColorArray(colors);
		snapPoint->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, 1));
		osg::ref_ptr<osg::StateSet> ss = snapPoint->getOrCreateStateSet();
		osg::ref_ptr<MyPoint> pointState = new MyPoint;
		pointState->setSize(9.f);
		ss->setAttribute(pointState);
		// 最后绘制
		// MainWindow::setScene会调用addPostDraw(snapPoint)
	}
	if (!d_ptr->m_root3D)
	{
		d_ptr->m_root3D = new osg::Group;

#ifdef _DEBUG
		osg::ref_ptr<osg::Group> scene = new osg::Group;
		d_ptr->m_root3D->addChild(scene);
#else
		osg::ref_ptr<osg::Group> scene = d_ptr->m_root3D;
#endif
		scene->addChild(d_ptr->m_rep3D);
		osg::ref_ptr<osg::Program> program = createViewADLightingProgram();
		osg::ref_ptr<osg::StateSet> ss1(scene->getOrCreateStateSet());
		ss1->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
		ss1->setAttributeAndModes(program, osg::StateAttribute::ON);
		setUniformForLightingProgram(ss1, osg::Vec3d(0, 0, -1), osg::Vec3d(1, 1, 1));
		//setUniformForFixedLightingProgram(ss1, osg::Vec3d(0, 0, 2), osg::Vec3d(1, 1, 1));
		//setShininessUniform(ss1, 16);
		osg::ref_ptr<osg::Uniform> detColorUniform = new osg::Uniform("detColor", osg::Vec4f(0, 0, 0, 0));
		ss1->addUniform(detColorUniform);
		osg::ref_ptr<osg::Uniform> ccwUniform = new osg::Uniform("ccw", true);
		ss1->addUniform(ccwUniform);

#ifdef _DEBUG
		osg::ref_ptr<osg::Group> sceneToShowNormal = new osg::Group;
		//选择时不必遍历调试节点树
		sceneToShowNormal->setNodeMask(NoPickMask);
		d_ptr->m_root3D->addChild(sceneToShowNormal);
		sceneToShowNormal->addChild(d_ptr->m_rep3D);
		osg::ref_ptr<osg::Program> program2 = createShowNormalProgram();
		osg::ref_ptr<osg::StateSet> ss2(sceneToShowNormal->getOrCreateStateSet());
		ss2->setAttributeAndModes(program2, osg::StateAttribute::ON);
		setNormalScaleUniform(ss2, 0.01f);
#endif

		Registry &reg = Registry::instance();
		auto widget3D = reg.get<QtOSGWidget>(widget3DID);
		auto pickHandler3D = widget3D->getPicker();
		d_ptr->m_osgHandles = new HandlesGeometry();
		d_ptr->m_osgHandles->setOSGWidget(widget3D);
		d_ptr->m_userFeedbackConn = pickHandler3D->connectUserFeedback([widget3D, this](const std::set<osg::Node*>& selected) {
			// called in render thread.
			//widget3D->pauseRenderThread();
			userFeedback(selected);
			//widget3D->resumeRenderThread();
		});
		scene->addChild(d_ptr->m_osgHandles);
	}
	return d_ptr->m_root3D.get();
}

QUndoStack* Scene::undoStack() const
{
	return d_ptr->m_undoStack;
}

void Scene::save(QJsonObject& json) const
{
	json["id"] = getID();
	QJsonArray childrenJson;
	for (auto id : d_ptr->m_sortedIdTable)
	{
		auto child = static_cast<Ent*>(getNodeByID(id));
		QJsonObject cjson;
		// archive construct itself
		child->save(cjson);
		childrenJson.append(cjson);
	}
	json["children"] = childrenJson;
}

void Scene::load(const QJsonObject& json)
{
	JsonReader reader;
	int oldId = json["id"].toInt();
	reader.idMap.insert(std::make_pair(oldId, getID()));
	auto constructsJson = json["children"].toArray();

	std::vector<std::shared_ptr<INode>> constructs;
	for (const auto& cjson : constructsJson)
	{
		auto cjsonobject = cjson.toObject();
		auto className = cjsonobject["class"].toString().toStdString();
		auto child = createInstance(className, this);
		if (child)
		{
			child->load(cjsonobject, reader);
			addChild(child);
		}
	}

	reader.buildRelations();
}

void Scene::newFile()
{
	const osg::Vec3d origin;
	auto xoy = std::make_shared<UCS>(this);
	xoy->setOrigin(origin);
	xoy->setKind(UCSKind::XOY);
	addChild(xoy);

	auto yoz = std::make_shared<UCS>(this);
	yoz->setOrigin(origin);
	yoz->setKind(UCSKind::YOZ);
	addChild(yoz);

	auto zox = std::make_shared<UCS>(this);
	zox->setOrigin(origin);
	zox->setKind(UCSKind::ZOX);
	addChild(zox);

	std::lock_guard<std::mutex> lock(d_ptr->m_mutex);
	d_ptr->m_ucses[(int)UCSKind::XOY] = xoy;
	d_ptr->m_ucses[(int)UCSKind::YOZ] = yoz;
	d_ptr->m_ucses[(int)UCSKind::ZOX] = zox;
}

bool snapPointHelper(QPointer<Qt3DPicker> picker, const osg::Vec3d& ptWCS, const osg::Vec3d& point, const double _offset, double& dist_2)
{
	bool result = false;
	auto tmp = picker->screenDist(toVector3D(point), toVector3D(ptWCS));
	double dx = abs(tmp.x());
	double dy = abs(tmp.y());
	if (dx <= _offset && dy <= _offset)
	{
		double i_dist_2 = dx * dx + dy * dy;
		if (i_dist_2 < dist_2)
		{
			dist_2 = i_dist_2;
			result = true;
		}
	}
	return result;
}

bool Scene::snapPoint(PointMatrix3D & point)
{
	if (!d_ptr->m_picker)
		return false;
	const double _offset = 8.00001;
	d_ptr->m_picker->updateMatrix();
	double dist_2 = _offset * _offset * 2;
	auto osgPt = point.ptWCS;
	bool ret = false;
	for (auto node : d_ptr->m_children)
	{
		auto ent = dynamic_cast<Ent*>(node.get());
		if (!ent)
			continue;
		auto points = ent->getAllWCSHandles();
		for (size_t i = 0; i < points.size(); ++i)
		{
			bool snapped = snapPointHelper(d_ptr->m_picker, osgPt, points[i], _offset, dist_2);
			if (snapped)
			{
				point.ptWCS = points[i];
				ret = true;
			}
		}
	}

	auto points = this->get3DXPoints();
	for (int i = 0; i < points.size(); ++i)
	{
		bool snapped = snapPointHelper(d_ptr->m_picker, osgPt, points[i], _offset, dist_2);
		if (snapped)
		{
			point.ptWCS = points[i];
			ret = true;
		}
	}

	for (int iu = 0; iu < 3; ++iu)
	{
		auto ucs = d_ptr->m_ucses[iu];
		if (ucs)
		{
			auto points = ucs->getXPoints();
			for (size_t i = 0; i < points.size(); ++i)
			{
				osg::Vec3d pt = osg::Vec3d(points[i], 0) * ucs->getMatrix();
				bool snapped = snapPointHelper(d_ptr->m_picker, osgPt, pt, _offset, dist_2);
				if (snapped)
				{
					point.ptWCS = pt;
					ret = true;
				}
			}
		}
	}

	point.ptECS = point.ptWCS * point.viewMatrix;
	if (ret)
	{
		showSnapPoint(point.ptWCS);
	}
	else
	{
		hideSnapPoint();
	}
	return ret;
}

void Scene::setPicker(Qt3DPicker * picker)
{
	assert(picker);
	d_ptr->m_picker = picker;
	d_ptr->m_osgHandles->setOSGWidget(picker->getOSGWidget());
}

Qt3DPicker * Scene::getPicker() const
{
	return d_ptr->m_picker;
}

void Scene::setCurrentUCS(UCSKind kind)
{
	if ((int)kind != d_ptr->m_currentUCSIndex)
	{
		auto ucs = getCurrentUCS();
		if (ucs)
			ucs->addFlags(UPDATE_GEOMETRY);
		d_ptr->m_currentUCSIndex = (int)kind;
		ucs = getCurrentUCS();
		if (ucs)
			ucs->addFlags(UPDATE_GEOMETRY);
	}
}

void Scene::setUCS(UCSKind kind, UCS * ucs)
{
	std::shared_ptr<UCS> spUCS;
	if (ucs)
	{
		spUCS = std::dynamic_pointer_cast<UCS>(ucs->shared_from_this());
	}
	std::lock_guard<std::mutex> lock(d_ptr->m_mutex);
	d_ptr->m_ucses[(int)kind] = spUCS;
}

void Scene::setUCSOrigin(const osg::Vec3d & pos)
{
	for (int kind = 0; kind < 3; ++kind)
	{
		auto ucs = d_ptr->m_ucses[kind];
		if (ucs)
			ucs->setOrigin(pos);
	}
}

std::shared_ptr<UCS> Scene::getCurrentUCS() const
{
	return d_ptr->m_ucses[d_ptr->m_currentUCSIndex];
}

std::vector<std::shared_ptr<UCS>> Scene::getUCS() const
{
	std::vector<std::shared_ptr<UCS>> ucses;
	std::lock_guard<std::mutex> lock(d_ptr->m_mutex);
	ucses.push_back(d_ptr->m_ucses[0]);
	ucses.push_back(d_ptr->m_ucses[1]);
	ucses.push_back(d_ptr->m_ucses[2]);
	return ucses;
}

UCSKind Scene::getCurrentUCSIndex() const
{
	return UCSKind(d_ptr->m_currentUCSIndex);
}

void Scene::userFeedback(const std::set<osg::Node*>& selected) const
{
	for (auto con : d_ptr->m_connections)
	{
		QObject::disconnect(con);
	}
	d_ptr->m_connections.clear();

	// update handles
	d_ptr->m_osgHandles->set(selected);

	// watch if any selection is dirty
	auto scene = static_cast<const Scene*>(this);
	QtOSGWidget* widget = scene->getPicker()->getOSGWidget();
	for (auto sel : selected)
	{
		auto obj = widget->getPickable(sel);
		if (obj)
		{
			auto con = QObject::connect(obj.get(), &INode::flagsChanged, [this]() {
				d_ptr->m_osgHandles->setDirty();
			});
			if (con)
			{
				d_ptr->m_connections.push_back(con);
			}
		}
	}
}

QJsonObject Scene::exportBrep()
{
	QJsonObject docObj;
	QJsonArray brepJson;
	auto children = getChildren();
	// 按ID排序
	std::vector<std::shared_ptr<INode>> childVec(children.begin(), children.end());
	sort(childVec.begin(), childVec.end(), [](const std::shared_ptr<INode>& e1, const std::shared_ptr<INode>& e2)
	{
		return e1->getID() < e2->getID();
	});
	for (const auto& child : childVec)
	{
		int nodeMask = child->getRep3D()->getNodeMask();
		if (nodeMask == D3SurfaceMask)
		{
			QJsonObject thisObject;
			std::shared_ptr<Polygon> polygon = std::dynamic_pointer_cast<Polygon>(child);
			if (polygon)
			{
				auto face = polygon->shape();
				if (!face.IsNull())
				{
					std::stringstream ss;
					BRepTools::Write(face, ss);
					thisObject["brep"] = ss.str().c_str();
					brepJson.append(thisObject);
				}
				continue;
			}
			std::shared_ptr<Surface> surface = std::dynamic_pointer_cast<Surface>(child);
			if (surface)
			{
				auto face = surface->shape();
				std::stringstream ss;
				BRepTools::Write(face, ss);
				thisObject["brep"] = ss.str().c_str();
				brepJson.append(thisObject);
			}
		}
	}
	docObj["surfaces"] = brepJson;
	return docObj;
}

std::vector<TopoDS_Face> Scene::getFaces() const
{
	std::vector<TopoDS_Face> result;
	auto children = getChildren();
	// 按ID排序
	std::vector<std::shared_ptr<INode>> childVec(children.begin(), children.end());
	sort(childVec.begin(), childVec.end(), [](const std::shared_ptr<INode>& e1, const std::shared_ptr<INode>& e2)
	{
		return e1->getID() < e2->getID();
	});
	for (const auto& child : childVec)
	{
		int nodeMask = child->getRep3D()->getNodeMask();
		if (nodeMask == D3SurfaceMask)
		{
			QJsonObject thisObject;
			std::shared_ptr<Polygon> polygon = std::dynamic_pointer_cast<Polygon>(child);
			if (polygon)
			{
				auto face = polygon->shape();
				if (!face.IsNull())
				{
					result.push_back(face);
				}
				continue;
			}
			std::shared_ptr<Surface> surface = std::dynamic_pointer_cast<Surface>(child);
			if (surface)
			{
				auto face = surface->shape();
				if(!face.IsNull())
					result.push_back(face);
			}
		}
	}
	return result;
}

void Scene::getRange(int & maxX, int & maxY, int & maxZ) const
{
	maxX = d_ptr->m_maxX;
	maxY = d_ptr->m_maxY;
	maxZ = d_ptr->m_maxZ;
}

void Scene::setRange(int maxX, int maxY, int maxZ)
{
	if (maxX > 0 && maxY > 0 && maxZ > 0)
	{
		d_ptr->m_maxX = maxX;
		d_ptr->m_maxY = maxY;
		d_ptr->m_maxZ = maxZ;
	}
}

void Scene::showAllFace()
{
	auto children = getChildren();
	for (auto& child:children)
	{
		std::shared_ptr<Polygon> polygon = std::dynamic_pointer_cast<Polygon>(child);
		if (polygon)
		{
			polygon->getRep3D()->setNodeMask(D3SurfaceMask);
			continue;
		}
		std::shared_ptr<Surface> surface = std::dynamic_pointer_cast<Surface>(child);
		if (surface)
			surface->getRep3D()->setNodeMask(D3SurfaceMask);
	}
}
